继续昨天的认证,今天来分析
在Spring Security中,授权是指对用户访问系统资源的限制。Spring Security提供了多种授权方式,包括基于角色的授权、基于表达式的授权、注解授权等。
基于角色的授权是指通过为用户分配不同的角色来限制其访问系统资源。Spring Security提供了一些默认的角色,如ROLE_USER和ROLE_ADMIN等,也支持开发者自定义角色。在Spring Security中,我们可以使用标签<intercept-url>和<http>来配置基于角色的授权。下面是一个例子:
<http> <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" /> </http>
上述配置表示只有拥有ROLE_ADMIN角色的用户才能访问/admin/**下的资源。
Spring Security还提供了注解授权、方法级授权等多种授权方式,可以根据具体需求进行选择和配置。
访问:得到了json的对象
(关于权限:就是往数据库中放字段,设置了就有)
MyUserDtealsService
package com.lya.securty.config; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.lya.securty.pojo.*; import com.lya.securty.service.IModuleService; import com.lya.securty.service.IRoleModuleService; import com.lya.securty.service.IUserRoleService; import com.lya.securty.service.IUserService; import com.lya.securty.service.impl.ModuleServiceImpl; import com.lya.securty.service.impl.RoleModuleServiceImpl; import com.lya.securty.service.impl.RoleServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Component; import java.util.List; import java.util.stream.Collectors; @Component //把他编程一个组件 public class MyUserDtealsService implements UserDetailsService { @Autowired private IUserService userService; @Autowired private IUserRoleService iUserRoleService; @Autowired private ModuleServiceImpl moduleService; @Autowired private RoleServiceImpl roleService; @Autowired private RoleModuleServiceImpl roleModuleService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userService.getOne(new QueryWrapper<User>().eq("username", username)); if (user == null) { throw new UsernameNotFoundException("用户无效"); } // 1.查出所有的身份, map遍历,返回新数据-->流,将流编程list // 2.多个id对应的权限,连接权限表 List<Integer> role_ids = iUserRoleService // 查出所有的身份 .list(new QueryWrapper<UserRole>().eq("user_id", user.getId())) // .stream().map(r->r.getUserId()); // 返回新数据-->流 .stream().map(UserRole::getUserId) // 将流编程list .collect(Collectors.toList()); // 用id查询身份对一的名字---比如1,2普通用户超级管理。 List<String> roles = roleService.list(new QueryWrapper<Role>().in("role_id", role_ids)) // 返回新数据-->流 .stream().map(Role::getRoleName) // 将流编程list .collect(Collectors.toList()); // 根据身份id查询权限 List<Integer> module_ids = roleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", role_ids)) // 返回新数据-->流 .stream().map(RoleModule::getModuleId) // 将流编程list .collect(Collectors.toList()); // 根据权限id查询权限 List<String> moudelse = moduleService.list(new QueryWrapper<Module>().in("id", module_ids)) // 返回新数据-->流 .stream().map(Module::getUrl) // .filter(Object::nonNull) // 将流编程list .collect(Collectors.toList()); // roles[权限] // modules 访问url roles.addAll(moudelse); List<SimpleGrantedAuthority> authorities = roles.stream().map(e -> { return new SimpleGrantedAuthority(e); }).collect(Collectors.toList()); user.setAuthorities(authorities); return user; } }
WebSecurityConfig
package com.lya.securty.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.lya.securty.resp.JsonResponseBody; import com.lya.securty.resp.JsonResponseStatus; import com.lya.securty.service.impl.UserServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.web.bind.annotation.ResponseBody; import javax.sql.DataSource; /** * @author all */ @Configuration//启动配置类 spring进行管理不然加载不了这个类 @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) //开启这个类 public class WebSecurityConfig { @Autowired private DataSource dataSource; //SpringBoot自己帶的一個序列化的类 @Autowired private ObjectMapper objectMapper; /** * 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置 */ @Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); tokenRepository.setDataSource(dataSource); // 设置为true要保障数据库该表不存在,不然会报异常哦 // 所以第二次打开服务器应用程序的时候得把它设为false tokenRepository.setCreateTableOnStartup(false); return tokenRepository; } // 加密类 @Bean // @Primary public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 配置密码编码器,首次采用明文密码方式进行比对校验 */ // @Bean // public PasswordEncoder passwordEncoder(){ // return NoOpPasswordEncoder.getInstance(); // } @Autowired private UserServiceImpl userService; @Autowired private MyUserDtealsService myUserDtealsService; // @Bean // public UserDetailsService userDetailsService() { // UserDetails admin = User.withUsername("admin") // .password(bcryptPasswordEncoder().encode("123456")) // .roles("ADMIN", "USER").build();//权限 // UserDetails user = User.withUsername("user") // .password(bcryptPasswordEncoder().encode("123456")) // .roles("USER").build(); // return new InMemoryUserDetailsManager(admin, user); // } /** * 获取AuthenticationManager(认证管理器),登录时认证使用(基于数据库方式) * @return provider * @throws Exception 异常 */ @Bean public AuthenticationManager authenticationManager() throws Exception { //创建DaoAuthenticationProvider DaoAuthenticationProvider provider=new DaoAuthenticationProvider(); //设置userDetailsService,基于数据库方式进行身份认证 provider.setUserDetailsService(myUserDtealsService); //配置密码编码器 provider.setPasswordEncoder(passwordEncoder()); return new ProviderManager(provider); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { //认证请求 http.authorizeRequests() //antMatchers匹配对应的路径 //permitAll允许访问 .antMatchers("/toLogin").permitAll() //hasRole具备身份 //hasAnyRole具备多个身份 .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasAnyRole("ADMIN", "USER") //anyRequest其余所有请求 .anyRequest() //authenticated认证 .authenticated() .and() .formLogin() //当前登录页面 .loginPage("/toLogin") //设置处理登录请求的接口 .loginProcessingUrl("/userLogin") //用户的数据参数 .usernameParameter("username") .passwordParameter("password") //转发 进入首页 地址栏不改变 //.successForwardUrl("/index") //成功处理器 .successHandler((req,resp,auth)->{ Object user = auth.getPrincipal(); JsonResponseBody.success(user); //重定向 跳转首页 // resp.sendRedirect("/index"); // 这里不要直接跳,应为适应前后端分离 // (通过流的方式,响应数据到前端) objectMapper .writeValue(resp.getOutputStream(),JsonResponseBody.success(user)); }) //失败处理器 .failureHandler((req,resp,ex)->{ //错误信息提示 req.setAttribute("msg",ex.getMessage()); //重定向 跳转登录 req.getRequestDispatcher("/toLogin").forward(req,resp); }) .and() .exceptionHandling() // .accessDeniedPage((req,resp,ex)->{ // //错误信息提示无权限 // objectMapper // .writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_LOGIN)); // }) // 五登录 .authenticationEntryPoint((req,resp,ex)->{ objectMapper .writeValue(resp.getOutputStream(),JsonResponseBody.other(JsonResponseStatus.NO_LOGIN)); }) .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/") .and() .rememberMe() // 指定 rememberMe 的参数名,用于在表单中携带 rememberMe 的值。 .rememberMeParameter("remember-me") // 指定 rememberMe 的有效期,单位为秒,默认2周。 .tokenValiditySeconds(300) // 指定 rememberMe 的 cookie 名称。 .rememberMeCookieName("remember-me-cookie") // 指定 rememberMe 的 token 存储方式,可以使用默认的 PersistentTokenRepository 或自定义的实现。 .tokenRepository(persistentTokenRepository()) // 指定 rememberMe 的认证方式,需要实现 UserDetailsService 接口,并在其中查询用户信息。 .userDetailsService(userService); // 防禦 http.csrf().disable(); http.exceptionHandling().accessDeniedPage("/noAccess"); return http.build(); } }
UserController(添加两个具有访问权限的)
package com.lya.securty.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @author all */ @Controller public class UserController { @RequestMapping("/toLogin") public String toLogin() { return "login"; } @RequestMapping("/userLogin") public String userLogin(String username, String password) { System.out.println("username=" + username + ",password=" + password); return "index"; } @RequestMapping("/admin/toAddUser") public String toAddUser() { return "admin/addUser"; } @RequestMapping("/admin/toListUser") public String toListUser() { return "admin/listUser"; } @RequestMapping("/admin/toResetPwd") public String toResetPwd() { return "admin/resetPwd"; } @RequestMapping("/admin/toUpdateUser") public String toUpdateUser() { return "admin/updateUser"; } @RequestMapping("/user/toUpdatePwd") public String toUpdatePwd() { return "user/updatePwd"; } @RequestMapping("/index") public String index(){ return "index"; } @ResponseBody @RequestMapping("/add") @PreAuthorize("hasAuthority('book:manager:add')") public String add() { return "订单新增"; } @ResponseBody @RequestMapping("/oradd") @PreAuthorize("hasAuthority('order:manager:add')") public String oradd() { return "订单新增"; } @RequestMapping("/noAccess") public String noAccess() { return "accessDenied"; } }
JsonResponseBody(返回前端的Json格式数据)
package com.lya.securty.resp; import lombok.Data; @Data public class JsonResponseBody<T> { private Integer code; private String msg; private T data; private Long total; private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data) { this.code = jsonResponseStatus.getCode(); this.msg = jsonResponseStatus.getMsg(); this.data = data; } private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data, Long total) { this.code = jsonResponseStatus.getCode(); this.msg = jsonResponseStatus.getMsg(); this.data = data; this.total = total; } public static <T> JsonResponseBody<T> success() { return new JsonResponseBody<T>(JsonResponseStatus.OK, null); } public static <T> JsonResponseBody<T> success(T data) { return new JsonResponseBody<T>(JsonResponseStatus.OK, data); } public static <T> JsonResponseBody<T> success(T data, Long total) { return new JsonResponseBody<T>(JsonResponseStatus.OK, data, total); } public static <T> JsonResponseBody<T> unknown() { return new JsonResponseBody<T>(JsonResponseStatus.UN_KNOWN, null); } public static <T> JsonResponseBody<T> other(JsonResponseStatus jsonResponseStatus) { return new JsonResponseBody<T>(jsonResponseStatus, null); } }
响应前端的msg
package com.lya.securty.resp; import lombok.Getter; @Getter public enum JsonResponseStatus { OK(200, "OK"), UN_KNOWN(500, "未知错误"), RESULT_EMPTY(1000, "查询结果为空"), NO_ACCESS(3001, "没有权限"), NO_LOGIN(4001, "没有登录"), LOGIN_FAILURE(5001, "登录失败"), ; private final Integer code; private final String msg; JsonResponseStatus(Integer code, String msg) { this.code = code; this.msg = msg; } }