|-导入pom 依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>|-配置config
*配置类添加@EnableWebSecurity 注解 *继承 WebSecurityConfigurerAdapter
@EnableWebSecurity //注解开启Spring Security的功能 public class SecurityConfig extends WebSecurityConfigurerAdapter {}*设置用户,基于内存
//用户验证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ //基于内存来存储用户信息 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER").and() //设置 .withUser("admin").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER","ADMIN"); }*设置http请求权限认证
//用户权限认证 @Override protected void configure(HttpSecurity http) throws Exception { http //这是认证的请求 .authorizeRequests() .antMatchers("/","/home","/login","/index","/error").permitAll() //这些请求 不需要认证 .antMatchers("/user/**").hasRole("USER") //user及以下路径,需要USER权限 .antMatchers("/admin/**").hasRole("ADMIN") .and() .formLogin().loginPage("/login").defaultSuccessUrl("/") //登录页面为login,登录成功跳转 "/" .and() .logout().logoutUrl("/logout").logoutSuccessUrl("/"); //等出路径为logout,登出成功跳转 "/" }*忽略 静态资源
/** * 核心过滤器配置,更多使用ignoring()用来忽略对静态资源的控制 */ @Override public void configure(WebSecurity web) throws Exception { web .ignoring() .antMatchers("/image/**"); }|- 新建类SecurityProvider类实现AuthenticationProvider接口 |- 实现authenticate()和supports()方法
/** * 自定义验证类,可以用作backdoor,例如输入:alex/任意密码就可以通过验证 */ @Component public class SecurityProvide implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String name = authentication.getName(); String password = authentication.getCredentials().toString(); //利用alex用户名登录,不管密码是什么都可以,伪装成admin用户 if (name.equals("alex")) { Collection<GrantedAuthority> authorityCollection = new ArrayList<>(); authorityCollection.add(new SimpleGrantedAuthority("ROLE_ADMIN")); authorityCollection.add(new SimpleGrantedAuthority("ROLE_USER")); return new UsernamePasswordAuthenticationToken( "admin", password, authorityCollection); } else { return null; } } @Override public boolean supports(Class<?> authentication) { return authentication.equals( UsernamePasswordAuthenticationToken.class); } }|-添加自定义的认证/验证方式
//用户验证 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ //基于内存来存储用户信息 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER").and() .withUser("admin").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER","ADMIN"); auth.authenticationProvider(securityProvide); }|-添加自定义参数名myusername和mypassword
//用户权限认证 @Override protected void configure(HttpSecurity http) throws Exception { http //这是认证的请求 .authorizeRequests() //这是不需要认证的请求 .antMatchers("/","/home","/login","/index","/error").permitAll() .antMatchers("/user/**").hasRole("USER") .antMatchers("/admin/**").hasRole("ADMIN") .and() .formLogin().loginPage("/login").defaultSuccessUrl("/") .usernameParameter("myusername").passwordParameter("mypassword") .and() .logout().logoutUrl("/logout").logoutSuccessUrl("/"); }|- entity中user对象实现 UserDetails 接口,并实现其下 7 个方法
*getAuthorities() /** * 从数据库中取出roles字符串后,进行分解,构成一个GrantedAuthority权限集合的List返回 */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> authorities =new ArrayList<>(); String[] split = roles.split(","); for (String s: split) { authorities.add(new SimpleGrantedAuthority(s)); //添加权限 } return authorities; } *获得 username 与 password 用户信息 @Override public String getPassword() {return password;} @Override public String getUsername() {return username;} *其余四个方法 都是以 true 返回|-service层实现类实现 UserDetailsService 接口,并实现 loadUserByUsername 方法
//查询数据库 用户信息 进行验证 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User s1 =userMapper.findByUsername(username); if (s1==null) throw new UsernameNotFoundException("数据库中无此用户!"); return s1; } |- SecurityConfig 中添加 service 验证方式 @Autowired UserServiceImpl userService //必须注入 实现UserDetailsService接口的实现层 ... auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder()); ...|-自定义鉴权管理器MyAccessDecisionManager 实现AccessDecisionManager+@Component
package com.deceen.config; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Iterator; /** * 鉴权控制器 */ @Component public class MyAccessDecisionManager implements AccessDecisionManager { //进行权限 验证 @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { //从configAttributes中获取访问资源所需要的角色,它来自MySecurityMetadataSource的getAttributes Iterator<ConfigAttribute> iterator = configAttributes.iterator(); //这是来自resource中 while (iterator.hasNext()){ ConfigAttribute authority = iterator.next(); String resourceRole = authority.getAttribute(); if (resourceRole.equals("ROLE_NONE")){ if(authentication instanceof AnonymousAuthenticationToken){ throw new BadCredentialsException("用户未登录"); } return; } //用户携带的 权限 -----> Authentication authentication,authentication.getAuthorities().getAuthority(); //资源中自定义的 权限 -----> Collection<ConfigAttribute> configAttributes.getAttribute() Iterator<? extends GrantedAuthority> iterator1 = authentication.getAuthorities().iterator(); //这是用户身上携带的角色 while (iterator1.hasNext()){ String userRole = iterator1.next().getAuthority(); if (userRole.equals("ROLE_ADMIN")){ return; }else if(userRole.equals(resourceRole)){ return; } } } throw new AccessDeniedException("你没有访问:"+object+"权限"); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class<?> clazz) { return true; } }|-权限元数据获取 实现 FilterInvocationSecurityMetadataSource+@Component
package com.deceen.config; import com.deceen.entity.Resource; import com.deceen.service.ResourceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.stereotype.Component; import org.springframework.util.AntPathMatcher; import java.util.Collection; import java.util.List; /** * 用于鉴权中,获取的元数据,resource中对应的 访问路径,权限 */ @Component public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Autowired private ResourceService resourceService; //本方法返回访问资源所需的角色集合 AntPathMatcher antPathMatcher = new AntPathMatcher(); //获取访问路径所对应的权限 @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { //从object中得到需要访问的资源,即网址 String url = ((FilterInvocation) object).getRequestUrl(); List<Resource> all = resourceService.findAll(); for (Resource resource:all) { if (antPathMatcher.match(resource.getUrl(),url)&& resource.getArrayRoles().length>0){ return SecurityConfig.createList(resource.getArrayRoles()); } } return SecurityConfig.createList("ROLE_NONE"); } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } }|-权限异常处理类 实现AccessDeniedHandler+@Component
package com.deceen.config; import com.deceen.entity.vo.JsonResult; import com.deceen.utils.ObjectMapperUtil; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; /** * 验证异常 处理类 */ @Component public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); httpServletResponse.setContentType("application/json,charset=UTF-8"); PrintWriter writer = httpServletResponse.getWriter(); JsonResult info = new JsonResult(500,e.getMessage(),null); writer.write(ObjectMapperUtil.toJson(info)); writer.flush(); writer.close(); } }|-修改配置类里的 用户权限认证方法
//用户权限认证 @Override protected void configure(HttpSecurity http) throws Exception { http //这是认证的请求 .authorizeRequests() // //这是不需要认证的请求 // .antMatchers("/","/home","/login","/index","/error").permitAll() // .antMatchers("/user/**").hasRole("USER") // .antMatchers("/admin/**").hasRole("ADMIN") //添加自定义 权限拦截器 FilterSecurityInterceptor 识别 .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O object) { object.setSecurityMetadataSource(mySecurityMetadataSource); object.setAccessDecisionManager(myAccessDecisionManager); return object; } }) .and() .formLogin().loginPage("/login_p").loginProcessingUrl("/login").permitAll() //1.自定义参数名称,与login.html中的参数对应 .usernameParameter("myusername").passwordParameter("mypassword") .and() .logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll() .and() .csrf().disable() .exceptionHandling().accessDeniedHandler(myAccessDeniedHandler); }*在Controller层,通过@AuthenticationPrincipal Principal principal(注解可省略)
@GetMapping("/user") public String user(@AuthenticationPrincipal Principal principal, Model model) { model.addAttribute("username",principal.getName()); return "user/user"; }*在Controller层,通过 HttpServletRequest
@RequestMapping(value = "/username", method = RequestMethod.GET) @ResponseBody public String name(HttpServletRequest request) { Principal principal = request.getUserPrincipal(); return principal.getName(); }*在任何地方,通过注入 IAuthenticationFacade
@Autowired private IAuthenticationFacade authenticationFacade; @RequestMapping(value = "/username", method = RequestMethod.GET) @ResponseBody public String nameSimpe() { Authentication authentication = authenticationFacade.getAuthentication(); return authentication.getName(); }*在任何地方,通过SecurityContextHolder中的.getContext().getAuthentication()
@GetMapping("/admin") public String admin( Model model) { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String username=null; if (principal instanceof UserDetails) { username= ((UserDetails) principal).getUsername(); }else if (principal instanceof Principal){ username= ((Principal) principal).getName(); } model.addAttribute("username",username); return "admin/admin";}
转载:https://zhuanlan.zhihu.com/p/47224331