一、说明
该实例采用 thymeleaf
模板,基于Session会话管理的基础权限管理例子
二、项目开始
- maven配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.5</version>
<scope>compile</scope>
</dependency>
</dependencies>
- 基础handler配置
/**
* 授权-没有权限处理器
* @author huzhihui
* @version $ v 0.1 2021/1/6 9:33 huzhihui Exp $$
*/
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
ResponseUtils.sendJsonData(httpServletResponse, ResponseMessage.failure("权限不足"));
}
}
/**
* 未登陆的处理器
* @author huzhihui
* @version $ v 0.1 2021/1/6 9:32 huzhihui Exp $$
*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
ResponseUtils.sendJsonData(httpServletResponse, ResponseMessage.failure("未登陆"));
}
}
/**
* 登陆认证失败处理器
* @author huzhihui
* @version $ v 0.1 2021/1/6 9:30 huzhihui Exp $$
*/
@Component
public class AuthenticationFailureHandlerImpl implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
ResponseUtils.sendJsonData(httpServletResponse, ResponseMessage.failure(e.getMessage()));
}
}
/**
* 登陆认证成功处理器
* @author huzhihui
* @version $ v 0.1 2021/1/6 9:29 huzhihui Exp $$
*/
@Component
public class AuthenticationSuccessHandlerImpl implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
ResponseUtils.sendJsonData(httpServletResponse, ResponseMessage.success(authentication));
}
}
/**
* 退出登录成功处理器
* @author huzhihui
* @version $ v 0.1 2021/1/6 9:34 huzhihui Exp $$
*/
@Component
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
ResponseUtils.sendJsonData(httpServletResponse, ResponseMessage.success("退出成功"));
}
}
上面的处理器对应各个流程处理
三、采用默认form登陆并用默认登陆配置AuthenticationProvider
如下
UserDetailsService
的实现类
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//此处应该是从数据库读取数据来验证
if(username.equals("admin")){
return new User(username,"$2a$10$99Mo0Mnpq6fyUTpsnLGVO.51WXKdY37YNMQFf88zx8.FOXYkcEzAS", AuthorityUtils.createAuthorityList("index","user"));
}
return new User(username,"$2a$10$99Mo0Mnpq6fyUTpsnLGVO.51WXKdY37YNMQFf88zx8.FOXYkcEzAS", AuthorityUtils.createAuthorityList("index"));
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private AuthenticationProviderImpl authenticationProvider;
@Autowired
private AccessDeniedHandlerImpl accessDeniedHandler;
@Autowired
private AuthenticationEntryPointImpl authenticationEntryPoint;
@Autowired
private AuthenticationFailureHandlerImpl authenticationFailureHandler;
@Autowired
private AuthenticationSuccessHandlerImpl authenticationSuccessHandler;
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 关闭csrf防护
.csrf().disable()
.headers().frameOptions().disable()
.and();
http
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
//登录处理
.formLogin() //表单方式
.loginPage("/login")
.loginProcessingUrl("/login/submit")
.defaultSuccessUrl("/index") //成功登陆后跳转页面
.failureHandler(authenticationFailureHandler)
.successHandler(authenticationSuccessHandler)
.permitAll()
.and();
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.and();
http.logout()
.logoutSuccessHandler(logoutSuccessHandler)
.and();
http
.authorizeRequests() // 授权配置
//无需权限访问
.antMatchers( "/css/**", "/error404").permitAll()
//其他接口需要登录后才能访问
.anyRequest().authenticated()
.and();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 1、默认的登陆处理器,
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
// 2、自己实现登陆逻辑处理
//auth.authenticationProvider(authenticationProvider);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
-
登陆页面/login默认路由就存在不用配置,由于我重新定义了登陆验证路由,所以把form表单提交的url改为loginProcessingUrl("/login/submit")
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登陆</title> </head> <body> <form class="form-signin" action="/login/submit" method="post"> <h2 class="form-signin-heading">用户登录</h2> <table> <tr> <td>用户名:</td> <td><input type="text" name="username" class="form-control" placeholder="请输入用户名"/></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password" class="form-control" placeholder="请输入密码" /></td> </tr> <tr> <td colspan="2"> <button type="submit" class="btn btn-lg btn-primary btn-block" >登录</button> </td> </tr> </table> </form> </body> </html>
四、采用自定义登陆的逻辑处理器
-
AuthenticationProvider
实现类
@Component
public class AuthenticationProviderImpl implements AuthenticationProvider {
private static final Logger log = LoggerFactory.getLogger(AuthenticationProviderImpl.class);
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userName = authentication.getName();
String password = authentication.getCredentials().toString();
log.info("userName:"+userName+" password:"+password);
UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//注解验证权限
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private AuthenticationProviderImpl authenticationProvider;
@Autowired
private AccessDeniedHandlerImpl accessDeniedHandler;
@Autowired
private AuthenticationEntryPointImpl authenticationEntryPoint;
@Autowired
private AuthenticationFailureHandlerImpl authenticationFailureHandler;
@Autowired
private AuthenticationSuccessHandlerImpl authenticationSuccessHandler;
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 关闭csrf防护
.csrf().disable()
.headers().frameOptions().disable()
.and();
http
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
//登录处理
.formLogin() //表单方式
.loginPage("/login")
.loginProcessingUrl("/login/submit")
.defaultSuccessUrl("/index") //成功登陆后跳转页面
.failureHandler(authenticationFailureHandler)
.successHandler(authenticationSuccessHandler)
.permitAll()
.and();
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.and();
http.logout()
.logoutSuccessHandler(logoutSuccessHandler)
.and();
http
.authorizeRequests() // 授权配置
//无需权限访问
.antMatchers( "/css/**", "/error404").permitAll()
//其他接口需要登录后才能访问
.anyRequest().authenticated()
.and();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 1、默认的登陆处理器,
//auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
// 2、自己实现登陆逻辑处理
auth.authenticationProvider(authenticationProvider);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
五、到此登陆逻辑就完成了,下面是接口权限校验注解
- 按照官方文档可以实现复杂的使用,此处就简单校验了是否拥有权限
@GetMapping(value = {"/index","/"})
@PreAuthorize(value = "hasAuthority('index')")
public String index(){
return "index page";
}
@GetMapping(value = {"/user"})
@PreAuthorize(value = "hasAuthority('user')")
public Object user(){
return SecurityContextHolder.getContext().getAuthentication();
}
@GetMapping(value = {"/sessionId"})
public String sessionId(HttpServletRequest request){
return "sessionId="+request.getSession().getId();
}
来源:oschina
链接:https://my.oschina.net/fellowtraveler/blog/4881718