一、Spring Security的基本配置

安全需要在设计网站之初就需要做好设计

可以做到:

  • 功能权限
  • 访问权限
  • 菜单权限

这些权限虽然用拦截器过滤器也能实现,但是很麻烦,所以我们一般使用框架实现

几个重要类:

  • WebSecurityConfigureAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebEnableSecurity模式

Spring Security的两个主要目标是“认证”和“授权”(即访问控制)

  • “认证”Authentication

  • “授权”Authorization

这个概念是通用的,而不只是在Spring Security中存在

流程:

导入依赖,是spring系列的框架

<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

写控制器

@RestController
public class Hello { @GetMapping("/hello")
public String getHello(){
return "hello security";
}
}

访问:http://localhost:8080/hello

用户名是user

密码为:

登录进去之后才进入了hello的放回界面

配置文件指定用户名和密码:

application.properties

spring.security.user.name=admin
spring.security.user.password=123456
spring.security.user.roles=admin

配置类实现更多的配置:

这是一种基于内存的配置,通过继承WebSecurityConfigurerAdapter

要与数据库认证区分开来,配置角色时不需要再添加“ROLE_”前缀

1、指定用户名和密码和职责(认证)

1、添加配置类

2、继承WebSecurityConfigurerAdapter

3、重写configure(AuthenticationManagerBuilder auth)方法

4、添加加密方式

@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { //springboot要求必须使用一种加密方式
//此处使用NoOpPasswordEncoder,即无密码加密方式
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//配置两个用户
auth.inMemoryAuthentication()
.withUser("admin").password("123456").roles("ADMIN","USER")
.and()
.withUser("wang").password("123").roles("USER");
}
}

2、配置受保护资源(授权)

重写的是configure(HttpSecurity http)方法

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//添加了三个成员,分别是数据库管理员、管理员、和用户
auth.inMemoryAuthentication()
.withUser("root").password("123456").roles("ADMIN","DBA")
.and()
.withUser("admin").password("123456").roles("ADMIN")
.and()
.withUser("wang").password("123456").roles("USER");
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//管理员--->管理员角色
.antMatchers("/admin/**").hasRole("ADMIN")
//用户--->用户角色或者管理员角色有其一即可
.antMatchers("/user/**").access("hasAnyRole('ADMIN','USER')")
//数据库--->管理员角色和数据库管理员角色都要有才能访问
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
//除去上面的请求模式需要特定的角色之外
// 其他请求都需要先经过认证,才能访问
.anyRequest().authenticated()
.and()
/*表单登录
* 登录url为"/login",注意这个“/”
* permitall表示所有与登录相关的接口都不需要认证就可以访问
* */
.formLogin().loginProcessingUrl("/login").permitAll()
.and()
//表示关闭csrf
.csrf().disable();
}

添加控制器--测试

@RestController
public class Hello { //管理员
@GetMapping("/admin/hello")
public String admin(){
return "hello admin";
} //用户
@GetMapping("/user/hello")
public String user(){
return "hello user";
} //数据库管理员
@GetMapping("/db/hello")
public String db(){
return "hello dba";
} //普通的hello
@GetMapping("/hello")
public String Hello(){
return "hello";
}
}

会发现只要登录了就可以访问/hello,还有这个用户对应的hello页面,其他以此类推

登录表单详细配置:

就是跟改上面授权中的.formLogin().loginProcessingUrl("/login").permitAll()这一段配置

//表单提交数据
.formLogin()
//下面这个是登录界面,此处没有写这个界面,所以用postman测试
.loginPage("/login_page")
//下面三行值得是提交信息给哪个url,而且用的是哪个参数名称
.loginProcessingUrl("/login")
.usernameParameter("name")
.passwordParameter("password")
//在这个成功操作的方法里面放一个实体静态类
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//从认证成功那里得到关键信息
Object principal = authentication.getPrincipal();
PrintWriter out = response.getWriter();
response.setStatus(200);
//这个map就是我们要放回的json信息
HashMap<String, Object> map = new HashMap<>();
map.put("status",200);
map.put("msg",principal); ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close(); }
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
response.setStatus(401);
HashMap<String, Object> map = new HashMap<>();
map.put("status",401);
if (exception instanceof LockedException){
map.put("msg","账户被锁,登陆失败");
}else if(exception instanceof BadCredentialsException){
map.put("msg","账户名或密码输入错误,登录失败");
}else if(exception instanceof DisabledException){
map.put("msg","账户被禁用,登陆失败");
}else if(exception instanceof CredentialsExpiredException){
map.put("msg","密码已过期,登陆失败");
}else if(exception instanceof AccountExpiredException){
map.put("msg","账户已过期,登陆失败");
}else{
map.put("msg","登陆失败");
} ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close(); }
})
.permitAll()

此处主要是返回一段json数据,数据提交给/login接口

所以可以使用postman测试

注销登录操作:

注销登录,就是类似于qq下线一样

在后面加上一个新的板块

.and()
//开启注销登录的配置
.logout()
//配置注销登录提交星系交给哪个接口
.logoutUrl("/logout")
.clearAuthentication(true)
.invalidateHttpSession(true)
.addLogoutHandler(new LogoutHandler() {
@Override
public void logout(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) {
//这个地方根据需要完成一些数据清除工作,如cookie
}
})
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect("/login_page");
}
})

多个Http Security同时配置:

略(见书)

二、密码加密

三、基于数据库认证

流程:

先创建好表

user、role、user_role三个表


![image-20210707125857051](https://img2020.cnblogs.com/blog/2309666/202107/2309666-20210715104156259-1119834196.png)
![image-20210707125857051](https://img2020.cnblogs.com/blog/2309666/202107/2309666-20210715104156259-1119834196.png)

填充数据


![image-20210707131719146](https://img2020.cnblogs.com/blog/2309666/202107/2309666-20210715104156482-81242353.png)

需要再整合mybatis

导入依赖

<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>

配置数据库

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/springboot

创建对应pojo类,Role类和User类

@Data
public class Role {
private Integer id;
private String name;
private String nameZh;
}
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private boolean enabled;
private boolean locked;
private List<Role> roles; public User() {
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public void setUsername(String username) {
this.username = username;
} public void setPassword(String password) {
this.password = password;
} public void setEnabled(boolean enabled) {
this.enabled = enabled;
} public boolean isLocked() {
return locked;
} public void setLocked(boolean locked) {
this.locked = locked;
} public List<Role> getRoles() {
return roles;
} public void setRoles(List<Role> roles) {
this.roles = roles;
}
/*======================下面为重写方法======================*/ //这个方法用于获取当前用户所具有的角色对象
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role:roles){
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
} @Override
public String getUsername() {
return username;
} @Override
public String getPassword() {
return password;
} //因为数据库中没有创建过期未过期的字段,故直接返回true
@Override
public boolean isAccountNonExpired() {
return true;
} @Override
public boolean isAccountNonLocked() {
return !locked;
} @Override
public boolean isCredentialsNonExpired() {
return true;
} @Override
public boolean isEnabled() {
return enabled;
}
}

创建UserMapper接口和UserMapper.xml

@Mapper
@Repository
public interface UserMapper {
//通过用户名去数据库找这个用户,如果没找到就返回一个账户不存在的错误
//如果找到了用户,就继续查找该用户对应的角色信息,并将获取到的用户信息返回,系统会自动去比对密码
User loadUseByUsername(String username); //通过用户id来找到这个其对应的角色
List<Role> getUserRolesByUid(Integer id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wang.dao.UserMapper">
<!--User loadUseByUsername(String username);-->
<select id="loadUseByUsername" parameterType="string" resultType="user">
select * from user where username = # {username}
</select> <!--List<Role> getUserRolesByUid(Integer id);-->
<select id="getUserRolesByUid" parameterType="integer" resultType="role">
select * from role r , user_role ur where r.id = ur.rid and ur.uid = #{id}
</select>
</mapper>

创建UserService

@Service
public class UserService implements UserDetailsService { @Autowired
UserMapper userMapper; //参数即为表单输入的参数
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.loadUseByUsername(username);
if (user == null){
throw new UsernameNotFoundException("账户不存在");
}
user.setRoles(userMapper.getUserRolesByUid(user.getId()));
return user;
}
}

配置Spring Security

User类怎么写

User类必须要实现UserDetails,里面有七个方法需要重写,

方法 解释
getAuthorities() 获取当前用户对象所具有的角色信息
getUsername() 获取当前用户名
isCredentialsNonExpired() 当前用户是否过期(字段)
isAccountNonLocked() 当前用户是否锁定(字段)
isEnabled() 当前账户是否可用(字段)
getPassword() 获取当前用户密码
isCredentialsNonExpired() 当前密码是否过期(密码字段)

getAuthorities()如何获取到角色信息的:

在User类中用户所具有的角色储存在了roles属性中,所以先创建一个SimpleGrantedAuthority的集合,

再遍历每个role,用他们的name创建一个个SimpleGrantedAuthority实体类,放到集合中去,最后放回这个集合即可。

UserService类怎么写的

必须要实现UserDetailsService类,并重写里面的loadUserByUsername方法,这个方法可以直接获取到输入的username,把这个username通过usermapper去查找

如果没找到,我们需要自定自己的输出错误

如果找到了,系统会自动会匹配他的密码,根据相关数据,返回不同的错误

在然后我们通过这个user的id通过usermapper的另一个方法找到他的角色,再把这个角色又赋值给这个user,再返回这个user实例,实现了通过username加载user

一个完整的基于内存的配置

package com.wang.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import javax.security.auth.login.CredentialException;
import javax.security.auth.login.CredentialExpiredException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap; //@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { //把这个实例放到IOC容器中去是为了使密码无加密
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//添加了三个成员,分别是数据库管理员、管理员、和用户
auth.inMemoryAuthentication()
.withUser("root").password("123456").roles("ADMIN","DBA")
.and()
.withUser("admin").password("123456").roles("ADMIN")
.and()
.withUser("wang").password("123456").roles("USER");
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//管理员--->管理员角色
.antMatchers("/admin/**").hasRole("ADMIN")
//用户--->用户角色或者管理员角色有其一即可
.antMatchers("/user/**").access("hasAnyRole('ADMIN','USER')")
//数据库--->管理员角色和数据库管理员角色都要有才能访问
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
//除去上面的请求模式需要特定的角色之外
// 其他请求都需要先经过认证,才能访问
.anyRequest().authenticated()
.and() //表单提交数据
.formLogin()
//下面这个是登录界面,此处没有写这个界面,所以用postman测试
.loginPage("/login_page")
//下面三行值得是提交信息给哪个url,而且用的是哪个参数名称
.loginProcessingUrl("/login")
.usernameParameter("name")
.passwordParameter("password")
//在这个成功操作的方法里面放一个实体静态类
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//从认证成功那里得到关键信息
Object principal = authentication.getPrincipal();
PrintWriter out = response.getWriter();
response.setStatus(200);
//这个map就是我们要放回的json信息
HashMap<String, Object> map = new HashMap<>();
map.put("status",200);
map.put("msg",principal); ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close(); }
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
response.setStatus(401);
HashMap<String, Object> map = new HashMap<>();
map.put("status",401);
if (exception instanceof LockedException){
map.put("msg","账户被锁,登陆失败");
}else if(exception instanceof BadCredentialsException){
map.put("msg","账户名或密码输入错误,登录失败");
}else if(exception instanceof DisabledException){
map.put("msg","账户被禁用,登陆失败");
}else if(exception instanceof CredentialsExpiredException){
map.put("msg","密码已过期,登陆失败");
}else if(exception instanceof AccountExpiredException){
map.put("msg","账户已过期,登陆失败");
}else{
map.put("msg","登陆失败");
} ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close(); }
})
.permitAll() .and()
//开启注销登录的配置
.logout()
//配置注销登录提交星系交给哪个接口
.logoutUrl("/logout")
.clearAuthentication(true)
.invalidateHttpSession(true)
.addLogoutHandler(new LogoutHandler() {
@Override
public void logout(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) {
//这个地方根据需要完成一些数据清除工作,如cookie
}
})
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.sendRedirect("/login_page");
}
}) .and()
.csrf()
.disable();
}
}

一个完整的基于数据库的配置

@Configuration
public class MyConfig extends WebSecurityConfigurerAdapter { @Autowired
UserService userService; @Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
} //配置角色的方法
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
} @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/db/**").hasRole("dba")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
.formLogin().loginProcessingUrl("/login").permitAll()
.and()
.csrf().disable();
}
}

角色继承

1、基于security框架的角色继承

在配置类中添加一个bean到IOC容器中去

这个配置保证了dba的权限包含了admin的,admin又包含了dba的

@Bean
RoleHierarchy roleHierarchy(){
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
String hierarchy = "ROLE_dba > ROLE_admin > ROLE_user";
roleHierarchy.setHierarchy(hierarchy);
return roleHierarchy;
}

2、动态配置权限

使用httpSecurity配置的认证授权规则还是不够灵活,无法实现资源和角色的动态调整,需要开发者自定义权限配置

(1)、数据库设计

在原来的数据库的基础上再增加资源表和资源角色表

(2)添加配置组件类

四、整合shiro

流程:

添加依赖

<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
<!--整合thymeleaf技术-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<!--不需要版本号-->
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>

配置基本信息

# shiro配置
# 开启shiro配置
shiro.enabled=true
# 开启shiroweb配置
shiro.web.enabled=true
# 表示登录地址,默认为/login.jsp
shiro.loginUrl=/login
# 登陆成功地址,默认为/
shiro.successUrl=/index
# 表示当登录之后到未授权界面默认跳转地址
shiro.unauthorizedUrl=/unauthorized
# 表示是否允许通过url参数实现会话跟踪,默认为true(如果支持cookie的话一般设为false)
shiro.sessionManager.sessionIdUrlRewritingEnabled=true
# 表示是否允许通过cookie实现会话跟踪,默认为true
shiro.sessionManager.sessionIdCookieEnabled=true

配置shiro

package com.wang.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm; import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class ShiroConfig { //注意这个地方的realm对应的包时shiro下的包
@Bean
public Realm realm(){
TextConfigurationRealm realm = new TextConfigurationRealm();
realm.setUserDefinitions("sang=123,user\n admin=123,admin");
realm.setRoleDefinitions("admin=read,write\n user=read");
return realm;
} @Bean
public ShiroFilterChainDefinition shiroFilterFactoryBean(){
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
//anon表示匿名,logout表示注销操作,authc表示需要登录操作
chainDefinition.addPathDefinition("/login","anon");
chainDefinition.addPathDefinition("/dologin","anon");
chainDefinition.addPathDefinition("/logout","logout");
chainDefinition.addPathDefinition("/**","authc");
return chainDefinition;
} //如果在thymeleaf中不使用shiro标签的话,不需要这个bean
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}

对应的control

package com.wang.control;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; @Controller
public class LoginControl { //dologin为登录接口
@PostMapping("doLogin")
public String doLogin(String username, String password, Model model){
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
/* 尝试登录
* 登录失败:带一个错误信息去登录界面
* 登录成功:重定向到/index
* */
try{
subject.login(token);
}catch (AuthenticationException e){
model.addAttribute("error","用户名或密码错误");
return "login";
}
//这个重定向其实无所谓,因为在配置文件中已经配置了
return "redirect:/index";
} @RequiresRoles("admin")
@GetMapping("/admin")
public String getAdmin(){
return "admin";
} @RequiresRoles(value = {"admin","user"},logical = Logical.OR)
@GetMapping("/user")
public String getUser(){
return "user";
}
}

配置对应的mvc配置

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//这些是不需要角色就可以访问到的接口
registry.addViewController("/login").setViewName("login");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/unauthorized").setViewName("unauthorized");
}
}

还有五个界面, login.html , index.html , admin.html , user.html , unauthorized.html

<!DOCTYPE html>
<html lang="en" xmlns:th=http://www.thymeleaf.org
xmlns:sec=http://www.thymeleaf.org/extras/spring-security
xmlns:shiro=http://www.pollix.at/thymeleaf/shiro>
<head> <title>Title</title>
</head>
<body>
<form th:action="@{/doLogin}" method="post">
username: <input type="text" name="username"><br/>
password: <input type="password" name="password"><br/>
<div th:text="${error}"></div>
<input type="submit" value="提交">
</form>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th=http://www.thymeleaf.org
xmlns:sec=http://www.thymeleaf.org/extras/spring-security
xmlns:shiro=http://www.pollix.at/thymeleaf/shiro>
<head> <title>Title</title>
</head>
<body>
<!--<shiro:principal/>可以查看用户名-->
<h3>hello,<shiro:principal/></h3>
<h4><a th:href="@{/logout}">注销操作</a></h4>
<h4><a shiro:hasRole="admin" th:href="@{/admin}">管理员界面</a></h4>
<h4><a shiro:hasAnyRoles="admin,user" th:href="@{/user}">普通用户界面</a></h4>
</body>
</html>

五、shiro的基于数据库的认证

流程:

需要一个通过id找到密码的userService

@Service
public class UserLoginService {
@Autowired
UserLoginMapper userLoginMapper; public UserLogin getUserLogin(String account){
return userLoginMapper.getUserLogin(account);
}
}

然后再ShiroConfig中自定义自己的Realm即可

@Configuration
public class ShiroConfig {
//注意这个地方的realm对应的包时shiro下的包
@Bean
public Realm realm(){
// TextConfigurationRealm realm = new TextConfigurationRealm();
// realm.setUserDefinitions("sang=123,user\n admin=123,admin");
// realm.setRoleDefinitions("admin=read,write\n user=read");
// return realm;
return new AuthRealm();
} @Bean
public ShiroFilterChainDefinition ShiroFilterFactoryBean(){
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
//anon表示匿名,logout表示注销操作,authc表示需要登录操作
chainDefinition.addPathDefinition("/login","anon");
chainDefinition.addPathDefinition("/dologin","anon");
chainDefinition.addPathDefinition("/logout","logout");
chainDefinition.addPathDefinition("/**","authc");
return chainDefinition;
} //如果在thymeleaf中不使用shiro标签的话,不需要这个bean
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
class AuthRealm extends AuthorizingRealm{ @Autowired
UserLoginService userLoginService; @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//认证
//从token中获取登录时的账户和密码
UsernamePasswordToken userToken = (UsernamePasswordToken)token;
String account = userToken.getUsername();
String passwordToken = new String(userToken.getPassword());
//获取数据库中的密码
String password = userLoginService.getUserLogin(account).getPassword();
//如果为空就是账号不存在,如果不相同就是密码错误,但是都抛出AuthenticationException,而不是抛出具体错误原因,免得给破解者提供帮助信息
if (passwordToken == null || !passwordToken.equals(password)){
return null;
}
//认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm
return new SimpleAuthenticationInfo(account,password,getName()); } @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//授权
//这个地方也可以配置数据库,进行id和角色的匹配
return null;
}
}

springboot-6-springSecurity的更多相关文章

  1. boke练习: springboot整合springSecurity出现的问题,传递csrf

    boke练习: springboot整合springSecurity出现的问题,传递csrf freemarker模板 在html页面中加入: <input name="_csrf&q ...

  2. boke练习: springboot整合springSecurity出现的问题,post,delete,put无法使用

    springboot 与 SpringSecurity整合后,为了防御csrf攻击,只有GET|OPTIONS|HEAD|TRACE|CONNECTION可以通过. 其他方法请求时,需要有token ...

  3. SpringBoot使用SpringSecurity搭建基于非对称加密的JWT及前后端分离的搭建

    SpringBoot使用SpringSecurity搭建基于非对称加密的JWT及前后端分离的搭建 - lhc0512的博客 - CSDN博客 https://blog.csdn.net/lhc0512 ...

  4. SpringBoot整合SpringSecurity简单实现登入登出从零搭建

    技术栈 : SpringBoot + SpringSecurity + jpa + freemark ,完整项目地址 : https://github.com/EalenXie/spring-secu ...

  5. 【使用篇二】SpringBoot集成SpringSecurity(22)

    SpringSecurity是专门针对基于Spring项目的安全框架,充分利用了依赖注入和AOP来实现安全管控.在很多大型企业级系统中权限是最核心的部分,一个系统的好与坏全都在于权限管控是否灵活,是否 ...

  6. SpringBoot+JWT+SpringSecurity+MybatisPlus实现Restful鉴权脚手架

    若图片查看异常,请前往掘金查看:https://juejin.im/post/5d1dee34e51d4577790c1cf4 前言 JWT(json web token)的无状态鉴权方式,越来越流行 ...

  7. SpringBoot 集成SpringSecurity JWT

    目录 1. 简介 1.1 SpringSecurity 1.2 OAuth2 1.3 JWT 2. SpringBoot 集成 SpringSecurity 2.1 导入Spring Security ...

  8. SpringBoot整合SpringSecurity示例实现前后分离权限注解

    SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证 作者:Sans_ juejin.im/post/5da82f066fb9a04e2a73daec 一.说 ...

  9. 9、SpringBoot整合之SpringBoot整合SpringSecurity

    SpringBoot整合SpringSecurity 一.创建项目,选择依赖 选择Spring Web.Thymeleaf即可 二.在pom文件中导入相关依赖 <!-- 导入SpringSecu ...

  10. SpringBoot整合SpringSecurity实现JWT认证

    目录 前言 目录 1.创建SpringBoot工程 2.导入SpringSecurity与JWT的相关依赖 3.定义SpringSecurity需要的基础处理类 4. 构建JWT token工具类 5 ...

随机推荐

  1. 一文搞定Samba云服务器配置(阿里云)

    石头门第五话桶子入侵SERN的时候,先是PowerShell连接远程服务器,然后突然就用图形化界面显示了远程的试验报告,就是来源于Samba(大概). 抱着以上的想法才买了个服务器并且配一下samba ...

  2. linux安装后配置

    1.1 系统设置(自测用,公司不需要) 1.1.1 Selinux系统安全保护 Security-Enhanced Linux – 美国NSA国家安全局主导开发,一套增强Linux系统安 全的强制访问 ...

  3. UF 公共类型

    Open C uc4400uc4403uc4404uc4406uc4409uf3192uf4401uf4402UF_add_callback_functionUF_allocate_memoryUF_ ...

  4. 【VBA】MsgBox用法

    MsgBox用法: Sub subMsgBox() Dim iok As Byte iok = MsgBox("是否XXX", vbYesNoCancel + vbQuestion ...

  5. 04:CSS(02)

    溢出属性 p { height: 100px; width: 50px; border: 3px solid red; /*overflow: visible; !*默认就是可见 溢出还是展示*!*/ ...

  6. Django基础之路由层

    内容概要 路由匹配 无名有名分组 反向解析 无名有名分组反向解析(难理解) 路由分发 名称空间 伪静态 内容详细 1 路由匹配 urls.py url()方法第一个参数其实是一个正则表达式 第一个参数 ...

  7. 题解 P5318 【【深基18.例3】查找文献】

    题目传送门 根据本蒟蒻细致粗略的分析 这明显是一道水题模(du)板(liu)题 可我竟然没有一遍AC; 为更好地食用本题解需要了解以下内容 1.dfs(大法师深搜) 2.bfs(冰法师广搜)/dij最 ...

  8. SpringCloud Alibaba实战(7:nacos注册中心管理微服务)

    源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 在上一节我们已经完成了Nacos Server的本地部署,这一节我们学习如何将Nac ...

  9. Oracle冷备

    概念:一致性的备份,也就是在数据库一致性关闭后做的备份,一般用:shutdown immediate方式关闭. 步骤:1.查看三大核心文件所在位置:数据文件,控制文件,日志文件 数据文件:select ...

  10. redis学习第一天

    不同于其他的常用关系型数据库,redis是一个非常轻便,体积小,存放键值对的数据库,常用于构建高性能,可扩展的Web应用程序. 这是我第一次接触redis,之前没有使用过,只听说过.因为刚毕业,找工作 ...