Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。
一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更加的简单。
一般Web应用的需要进行认证和授权。
认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户
授权:经过认证后判断当前用户是否有权限进行某个操作
而认证和授权也是SpringSecurity作为安全框架的核心功能。
Basic认证是一种较为简单的HTTP认证方式,客户端通过明文(Base64编码格式)户名和密码到服务器进行认证,通过常需要配合HTTPS来保证传输的安全
@Component @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /* /新增Security账户 授权的账户 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //需要经过wzx auth.inMemoryAuthentication().withUser("wzx").password("456").authorities("/"); } @Override protected void configure(HttpSecurity http) throws Exception { //配置认证方式 1.token 2.form表单 http.authorizeHttpRequests().antMatchers("/**").fullyAuthenticated().and().httpBasic(); } }
From 表单模式 适合于传统模式项目 前端和后端都是我们Java开发人员自己实现。Vue+SpringBoot
@Component @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /* /新增Security账户 授权的账户 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //需要经过wzx auth.inMemoryAuthentication().withUser("admin").password("admin").authorities("/"); } @Override protected void configure(HttpSecurity http) throws Exception { //配置认证方式 1.token 2.form表单 设置为BasicHttp认证 http.authorizeHttpRequests().antMatchers("/**").authenticated().and().formLogin(); } @Bean public static NoOpPasswordEncoder passwordEncoder(){ return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); } }
在企业管理系统平台中,会拆分成n多个不同的账号,每个账号对应不同的接口访问权限,
比如
账号admin所有接口都可以访问;
其他账号只能根据自己的权限进行访问;
//静态配置 @Component @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { /* /新增Security账户 授权的账户 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //需要经过wzx auth.inMemoryAuthentication().withUser("admin").password("admin").authorities("add"); auth.inMemoryAuthentication().withUser("wzx").password("wzx").authorities("update"); //对当前账户进行授权 } //拦截 @Override protected void configure(HttpSecurity http) throws Exception { //配置认证方式 1.token 2.form表单 //http.authorizeHttpRequests().antMatchers("/**").authenticated().and().formLogin(); //.antMatchers("add")方法中设置接口名称 .hasAnyAuthority("add")方法中防止接口所对应的权限 http.authorizeHttpRequests() .antMatchers("/user/add").hasAnyAuthority("add") .antMatchers("/user/delete").hasAnyAuthority("delete") .antMatchers("/user/show").hasAnyAuthority("show") .antMatchers("/user/update").hasAnyAuthority("update") .antMatchers("/**").authenticated().and().formLogin(); } @Bean public static NoOpPasswordEncoder passwordEncoder(){ return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance(); }
统一返回异常类
/* * 统一返回错误异常类 * */ @RestController public class ErrorController { @RequestMapping("/error/403") public String error(){ return "权限不足"; } }
@Configuration public class WebServerAutoConfig { @Bean public ConfigurableServletWebServerFactory webServerFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400"); ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401"); ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403"); ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"); ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415"); ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500"); factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500); return factory; } }
//拦截 @Override protected void configure(HttpSecurity http) throws Exception { //配置认证方式 1.token 2.form表单 //http.authorizeHttpRequests().antMatchers("/**").authenticated().and().formLogin(); //.antMatchers("add")方法中设置接口名称 .hasAnyAuthority("add")方法中防止接口所对应的权限 http.authorizeHttpRequests() .antMatchers("/user/add").hasAnyAuthority("add") .antMatchers("/user/delete").hasAnyAuthority("delete") .antMatchers("/user/show").hasAnyAuthority("show") .antMatchers("/user/update").hasAnyAuthority("update") //可以允许login 登录界面不被拦截 .antMatchers("/login.html").permitAll() //设置自定义登录页面 .antMatchers("/**").authenticated().and().formLogin() .loginPage("/login.html") //自定义登录界面 .loginProcessingUrl("/login") // 登录接口,与form表单提交链接对应 // .defaultSuccessUrl("/index.html") //登录成功跳转的界面 .and().csrf().disable(); }
Title
它就是用db查询直接去数据库区查询所具备的所有权限规则和对用户进行授权
1.实现动态拦截
上面是所谓的静态权限拦截,那么动态拦截就是用db查询去数据库查询所有的权限和规则
//拦截 @Override protected void configure(HttpSecurity http) throws Exception { //配置认证方式 1.token 2.form表单 //动态拦截 ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry authorizeHttpRequests = http.authorizeRequests(); //查询数据库中所有的权限 List permList = permMapper.queryAllPerm(); permList.forEach( p -> { //动态将接口和所对应的规则匹配 authorizeHttpRequests.antMatchers(p.permUrl).hasAnyAuthority(p.permDesc); }); authorizeHttpRequests //可以允许login 登录界面不被拦截 .antMatchers("/login.html").permitAll() .antMatchers("/test.do").permitAll() //设置自定义登录页面 .antMatchers("/**").authenticated().and().formLogin() .loginPage("/login.html") //自定义登录界面 .loginProcessingUrl("/login") // 登录接口,与form表单提交链接对应 .defaultSuccessUrl("/index.html") //登录成功跳转的界面 .and().csrf().disable(); }
2.实现动态授权
首先实现动态授权要穿件一个MemberDetilsService实现UserDetilsService接口,其中loadUserByUsername() 方法是登录后加载该用户所具备的权限,用户实体类也必须要实现UserDetils 接口。
@Service public class MembersDetailsService implements UserDetailsService { @Autowired UserMapper userMapper; @Autowired PermMapper permMapper; //登录加载user @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //1.在登录的时候 调用该方法 userName查询账户是否存在 在验证账户的密码 User user = userMapper.queryByUserName(username); if (user == null) { return null; } //2.再根据账户的userID 关联查询 角色对应的权限 动态进行添加授权 ListuserPermList = permMapper.queryByUserName(username); //设置权限 ArrayList grantedAuthorities = new ArrayList<>(); userPermList.forEach( item -> { grantedAuthorities.add(new SimpleGrantedAuthority(item.permDesc)); }); user.setAuthorities(grantedAuthorities); return user; } }
用户实体类
@Data @AllArgsConstructor @NoArgsConstructor //必须要实现UserDetails接口 public class User implements UserDetails { private Integer id; //用户id private String username; //用户账号 private String password; //用户密码 private Listrole; //用户所有的权限 private List authorities = new ArrayList<>(); @Override public Collection extends GrantedAuthority> getAuthorities() { return authorities; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
然后去配置动态授权,其中要注意用加密工具对密码实现加解密
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //对当前账户进行授权 auth.userDetailsService(membersDetailsService).passwordEncoder(new PasswordEncoder() { @Override public String encode(CharSequence rawPassword) { //使用Md5加密算法 return MD5Util.convertMD5((String) rawPassword); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { //使用MD5加密后 与数据库中的密码MD5 后验证 String rawPass = MD5Util.convertMD5((String) rawPassword); boolean result = rawPass.equals(MD5Util.convertMD5(encodedPassword)); return result; } }); }
MD5加密工具
public class MD5Util { /** * 使用 MD5算法加密生成32位md5码 * @param str * @return */ public static String string2MD5(String str) { byte[] secretBytes = null; try { secretBytes = MessageDigest.getInstance("md5").digest( str.getBytes()); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("没有md5这个算法!"); } String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字 // 如果生成数字未满32位,需要前面补0 for (int i = 0; i < 32 - md5code.length(); i++) { md5code = "0" + md5code; } return md5code; } /** * 可逆的加密解密算法,执行一次加密,两次解密 * @param str * @return */ public static String convertMD5(String str){ char[] a = str.toCharArray(); for (int i = 0; i < a.length; i++){ a[i] = (char) (a[i] ^ 't'); } String s = new String(a); return s; } }
什么是oauth2?
OAuth2.0是一个授权协议,他允许软件应用代表(而不是充当)资源拥有者去访问资源拥有者的资源。应用向资源拥有者请求授权,然后去的令牌(Token),并用他来访问资源,并且资源拥有者不用向应用提供用户名和密码等敏感数据。
OAUTH角色划分
Resource Server:被授权访问的资源
Authotization Server: OAUTH2认证授权中心
Recource Owner:用户
Client: 使用API的合作伙伴
Oauth应用场景
第三方联合登录 如QQ、微信
开放接口 蚂蚁金服、腾讯开放接口