- 安全管理
- Spring Security
- 手工配置用户名和密码
- HTTPSecurity配置
- 登录表单详细配置
- 注销登录配置
- 多个HTTPSecurity
- 密码加密
- 方法安全
- 基于数据库的认证
- 角色继承
- 动态配置权限
- OAuth2简介
- SpringSecurity结合OAuth2
- 整合Shiro方式一
- 整合Shiro方式二
- Spring Security使用JSON登录
- Spring Security(重量级的权限管理框架)shiro(轻量级权限管理框架)
创建SpringBoot项目,勾选Security下的Spring Security依赖 项目中的所有接口全都被保护起来了 创建一个controller
@RestController
public class HelloController{
@GetMapping("/hello")
public String hello(){
return "hello"
}
}
启动项目,访问http://localhost:8080/hello,页面自动跳转到http://localhost:8080/login 用户名默认是user,密码是后台控制台上面的Using generated sercurity password:.....
- 手工配置用户名和密码
数据库中配置====建议 或者配置文件中配置application.properties或者代码配置
//application.properties配置
spring.security.user.password=123
=zenghao
spring.security.user.roles=admin
下次启动项目的时候,就用配置好的zenghao 123登录
//代码配置
创建一个config目录和一个controller目录
config中创建一个SecurityConfig类继承WebSecurityConfigurerAdpter
@Configuration
Public class SecurityConfig extends WebSecurityConfigurerAdapter {
//从spring5开始密码需要加密,下面先用一种过期的方式配置,密码不加密
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception {
auth.inMemoryAuthentication()
.withUser("zenghao").password("123").roles("admin") //密码要加密
.and()
.withUser("zhangsan").password("789").roles("user");
}
}
- HttpSecurity配置
- 登录表单详细配置
- 注销登录配置
@Configuration
Public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception {
auth.inMemoryAuthentication()
.withUser("zenghao").password("123").roles("admin") //密码要加密
.and()
.withUser("zhangsan").password("789").roles("user");
}
@Override
protected void configure(HttpSecurity http)throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasAnyRole("admin","user")
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
// .loginPage("/hello") 配置登录页面
// .usernameParameter("uname")
// .passwordParameter("passwd")
/*
登录表单的详细配置
*/
.successHandler (new AuthenticationSuccessHandler(){ 登录成功跳转,可返回一段json
@Override
public void onAuthenticationSuccess(HttpServletRequest req,HttpServletResponse resp,Authentication authentication)
throws IOException,ServletException {
resp.setContentType("application/json:charset=utf-8");
PringWriter out = resp.gerWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",200);
map.put("mag",authentication.getPPrincipal());
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
//登录失败的处理
.failureHandler(new AuthenticationFailureHandler(){
@Override
public void AuthenticationFailure(HttpServletRequest req,HttpServletResponse resp,AuthenticationException e)
throws IOException,ServletException {
resp.setContentType("application/json:charset=utf-8");
PringWriter out = resp.gerWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",401);
if(e instanceof LockedException){
map.put("msg","账户被锁定,登录失败");
}else if(e instanceof BadCredentialsException){
map.put("msg","用户名或密码输入错误,登录失败");
}else if(e instanceof DisabledException){
map.put("msg","账户被禁用,登录失败");
}else if(e instanceof AccountExprireException){
map.put("msg","账户过期,登录失败");
}else if(e instanceof CredentialsExprieException){
map.put("msg","密码过期,登录失败");
}else{
map.put("msg","登录失败");
}
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
})
.permitAll()
/*
注销登录配置,发送注销登录请求
http://locahost:8080/logout
*/
.and()
.logout()
.logoutUrl("/logout")
public void onLogoutSuccess(HttpServletRequest req,HttpServletResponse resp,Authentication authentication)
throws IOException,ServletException {
resp.setContentType("application/json:charset=utf-8");
PringWriter out = resp.gerWriter();
Map<String,Object> map = new HashMap<>();
map.put("status",200);
map.put("mag","注销登录成功");
out.write(new ObjectMapper().writeValueAsString(map));
out.flush();
out.close();
}
.and()
.csrf().disable(); //测试的时候可以关闭
}
}
在controller目录创建一个helloController
@RestController
public class HelloController {
@GetMappring("/admin/hello")
public String admin(){
return "hello admin";
}
@GetMapping("/user/hello")
public String user(){
return "hello user";
}
}
postman中可以: http://localhost:8080/doLogin?username=zenghao&password=123
- 多个HTTPSecurity
创建一个MultiHTTPSecurityConfig
@Configuration
public class MultiHTTPSecurityConfig {
@Bean
PasswordEncoder passwordEncoder(){
return NoOPassword.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception {
auth.inMemoryAuthentication()
.withUser("zenghao").password("123").roles("admin") //密码要加密
.and()
.withUser("zhangsan").password("789").roles("user");
}
@Configuration
@Order(1) //指定优先级
public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception{
http.antMatcher("/admin/**").authoruzeRequests().anyRequest().hasAnyRole("admin");
}
@Configuration
@Order(2)
public static class OtherSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http)htrows Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("doLogin")
.permitAll()
.and()
.csrf().disable();
}
}
}
}
- 密码加密
@Test
public void test(){
BCryptPasswordEncoder encoder = new BcrtptPasswordEncoder();
Systerm.out.println(encoder.encode("123"));//运行,控制台获取加密后的密文,假设是hihadgooi&&^khdgaoi@
}
上面就可改成
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception {
auth.inMemoryAuthentication()
.withUser("zenghao").password("hihadgooi&&^khdgaoi@").roles("admin") //密码要加密 BcrtptPasswordEncoder
.and()
.withUser("zhangsan").password("hihadgooi&&^khdgaoi@").roles("user");
}
- 方法安全
- 直接在方法加注解,确保方法安全 前置条件:在Security配置文件上加一个注解
@EnableGlobalMethodSecurity(prePostEnabled=true,securedEnabled=true)
创建一个service
@Service
public class MethodService{
@preAuthorize("hsRole('admin')") //表示只有admin的角色才能访问
public String admin(){
return "hello admin";
}
@Secured("ROLB_user") //user的角色才能访问
public String user(){
return "hello user";
}
@PreAuthorize("hasAnyRole('admin','user')") //admin,user的角色都可以访问
public String hello(){
return "hello ";
}
}
创建一个Controller测试
@Autowired
MethodService methodService;
@GetMapping("/hello1")
public String hello1(){
return methodService.admin(); //admin可以访问
}
@GetMapping("/hello2")
public String hello2(){
return methodService.user(); //user可以访问
}
@GetMapping("/hello3")
public String hello3(){
return methodService.hello(); //admin、user都可以访问
}
- 基于数据库的认证,加载数据库的用户实现鉴权的操作
- 创建springboot项目,勾选 web依赖、spring Security依赖,mysql 依赖,mybatis依赖 pom文件中引入druid和确定数据库版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>5.1.27</version>
</dependency>
//由于mapper.xml放在mapper目录下,所以还需配
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
配置application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/zenghao
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
创建Bean,【User\role】,实现get\set()
publicr class User implements UserDeatils{ //相当于一个规范
private Integer id;
private String username;
private String password;
private Boolean enabled; //是否有效
private Boolean locked; //是否锁定
private List<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities(){
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for(Role role :roles){
authorities.add(new SimpleGrantedAuthority("Role_"+role.getName()));
}
return authorities;
}
}
public class Role {
private Integer id;
private String name;
Private String nameZh; //中文名字
}
创建一个UserService
@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(Styring username) throws UsernameNotFoundException{
User user= userMapper.loadUserByUsername(username);
if(user==null){
throw new UsernameNotFoundException("用户不存在")
}
user.setRoles(userMapper.getUserRolesById(user.getId()));
return user;
}
}
创建一个UserMaper
@Mapper
public Interface UserMaper {
User.loadUserByUsername(String username);
List<Role> getUserRolesById(Integer id);
}
创建UserMapper.xml
<mapper namespace="org.zenghao.mapper.UserMapper">
<selcet id="loadUserByUsername" resultType="org.zenghao.ben.User">
select * from user where username=#{username}
</select>
<select id="getUserRolesById" resultType="org.zenghao.bean.Role">
select * from role where id in (select rid from user_role where uid=#{id})
</select>
</mapper>
创建一个SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
autn.userDeatilsService(userService);
}
@Override
protected void configure(HttpSecurity http)throws Exception{
http.authorizeRequests()
.antMatchers("/dba/**").haRole("dba")
.antMatchers("/admin/**").hasRole("admin")
.antmatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
.formLogin()
.oermitAll()
.and()
.scrf().disable();
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
创建一个Contoller
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
@GetMapping("/dab/hello")
public String hello(){
return "hello dba";
}
@GetMapping("/admin/hello")
public String hello(){
return "hello admin";
}
@GetMapping("/user/hello")
public String hello(){
return "hello user";
}
- 角色继承
@Bean
RoleHierarchy ro;eHierarchy(){
RolerHierarchyImpl roleHierarchy = new RoleHierarvhyImpl();
String hierarchy="ROLE_dba>ROLE_admin \n ROLE_admin > ROLE_user";
roleHiserarchy.setHierarchy(hierarchy);
return roleHierarchy;
}
- 动态权限配置
- 把权限的配置放在数据库里面 创建一个类实现FilterInvocationSecurityMetadataSource里面的方法
- 更具请求的地址分析具备哪一个角色
@Compoent
public class MyFilter implements FilterInvocationSecurityMetadataSource{
//路径分割匹配符
AntPathMaMatcher pathMatcher = new AntPathMaMatcher ();
@Autowired
MenuService muenService;
@Override
public Collection<ConfigAtteribute> getAttributes(Onject 0)throws Illegal;ArtumentExcrption {
String requestUrl = ((FilterInvocation)o).getRequestUrl();
List<menu> allMenus = menuService.getAllMenus();
for(Menu menu : allMenus) {
if(pathMatcher.match(menu.getPattern(),requestUrl)){
List<Role> roles = menu.getRoles();
String[] rolesStr = nre String[roles.size()];
for (int i=0;i<roles.size();i++){
rolesStr[i]=roles.get(i).getName();
}
return SecurityConfig.createList(rolesStr);
}
}
return SecurityConfig.createList("ROLE_login");
}
@Overrid
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@OVerride
public boolean supports(Class<?> aClass) {
return null;
}
}
@Componet
public class MyAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication,Object o,Collection<ConfigAtteribute> collection) throws
AccessDeniedException ,InsufficientAuthenticationException {
for(ConfigAttribute attribute :collection){
if(:ROLE_login".equals(attribute.getAttribute())){
if(authentication instanceof AnonymousAuthenticationToken){
throw new AccessDeniedException("非法请求!");
}else{
return;
}
}
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for(GrantedAuthority authority:authorities) {
//如果刚好有一种需要的角色
if(authority.getAuthority().equals(attribute.getAttribute())) {
return;
}
}
}
throw new AccessDeniedException("非法请求!");
}
@Override
public boolean supports(ConfigAtteribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
在securityConfig类中配置上面两个类
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>(){
@Override
public <o extends FilterSecurityInterecptor> o postProcess(O o) {
o.setAccessDecisionManager(myAccessDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
@Componet
public class MyAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication,Object o,Collection<ConfigAtteribute> collection) throws
AccessDeniedException ,InsufficientAuthenticationException {
for(ConfigAttribute attribute :collection){
if(:ROLE_login".equals(attribute.getAttribute())){
if(authentication instanceof AnonymousAuthenticationToken){
throw new AccessDeniedException("非法请求!");
}else{
}
- OAuth2简介
- Spring Security结合OAuth2
允许用户第三方登录访问一个地方的资源,例如QQ登录网易云音乐 通过提供一个Token,特定时间访问特定资源 四个基本的角色: 资源所有者-用户 客户端-第三方应用 授权服务器:提供一个令牌给第三方应用 资源服务器: 六个步骤,四种不同的授权模式
创建一个项目,添加web依赖,添加Spring security依赖 在pom中加入OAuth2依赖
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.6RELEASE</version>
</denpendency>
redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId?spring-boot-starter-data-redis<artifactId>
</dependency>
在 application.properties中配置
spring.redis.host:localhost
spring.redis.port=6379
spring.redis.password=123
spring.redis.database=0
配置一个授权服务器 ,创建一个AuthorizationServerConfig
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("password")
.withauthorizedGrantTypes("password","refresh_token")
.accessTokenValiditySeconds(1800) //设置失效时间30分钟
.resourceIds("rid")
.scopes("all")
.secret("hadiogaglkds*dihih$ihdfa@#$"); //置放passwordEncoder转换123的加密值
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDeatilsService(userDeatilsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security)throws Exception {
security.allwFormAuthenticationForClients();
}
}
配置一个资源服务器 ResourceServerConfig
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception{
resources.resourceId("rid").stateless(true);
}
@Override
public void configre(HttpSecurity http) throws Exception {
http.authoruzeRequests().antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated();
}
}
创建一个SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager()
}
@Bean
@Override
protected UserDeatilsService userDeatilsService(){
return super.userDeatilsService()
}
@Override
protected void configre(AuthenticationManagerBuilder auth )throws Exception {
auth.inMemoryAuthentication()
.withUser("zenghao").password("hadiogaglkds*dihih$ihdfa@#$").roles("admin")
.and()
.withUser("zhansan")
.password("hadiogaglkds*dihih$ihdfa@#$")
.roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception{
http.antMatcher("/pauth/**)
.authorizeRequests()
.antMatchers("/oauth/**")/permitAll()
.and().scrf().disable();
}
}
创建Controller
@RestController
public class HelloController {
@GetMapping("/admin/hello")
public String admin(){
return "hello admin";
}
@GetMapping("/user/hello")
public String admin(){
return "hello user";
}
@GetMapping("/hello")
public String admin(){
return "hello";
}
}
- 整合Shiro方式一
- 在pom中手动添加
dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
创建一个类
public class MyRealm extends AuthorizingReam {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection princiipals) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticaionToken token)throws AuthentiticationException {
String username = (String) token.getPrincipal();
if("zenghao".equals(username)) {
return new SimpleAuthenticationInfo(username,"123",getName());
}
return null;
}
}
创建一个Shiro的配置文件
@Configuration
public class ShiroConfig {
@Bean
MyRealm myRealm(){
return new MyRealm();
}
@Bean
SecuirtuManager securityManager(){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager ();
manager.setRealm(myRealem());
return manager;
}
@Bean
ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean ();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login");
bean.setSuccessUrl(".index");
Map<String,String> map = new LinkedHashMap<>();
map.put("/dologin","anon");
map.put("/**","authc");
bean.setFilterChainDefinitionMap(map);
return bean;
}
}
创建一个Controller
@RestController
public class HelloController {
@GetMappin("/login")
public String login(){
return "plealse login";
}
@PostMapping("/doLogin")
public String doLogin(String username,String password){
Subject subject = SecurityUtils.getSubject();
try{
subject.login(new UsernamePasswordToken(username,password));
System.out.println("success");
}catch(AuthenticationException e){
e.printStackTrace()
System.out.println("fail>>"+e.getMessage());
}
}
@GetMapping("/hello")
public String hello(){
return "hello shrio";
}
}
- 整合Shiro方式二
- 在pom中手动添加
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-sprin-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
在application.properties中配置
shiro.enabled=true
shiro.unauthorizedUrl=/unauthorizedUrl
shiro.web.enabled=true
shrio.successUrl=/success
shiro.loginUrl=/login
//选配
shiro.sessionManager.sessionIdUrlRewritingEnabled=true //是否支持放入session里俩民
shiro.seessionManager.sessionIdCookieEnabled=true
创建一个ShiroConfig
@Configuration
public class ShiroConfig {
//配置角色权限
@Bean
Realm realm() {
TextConfigurationReal realm = new TextConfigurationRealm();
realm.setUserDefinitions("zenghao,user \n admin=123,admin")
realm.setRoleDefinitions("admin=read,write \n user=read");
return realm;
}
//配置拦截规则
@Bean
ShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
definition.addPathDefinition("/doLogin","anon");
definition.addPathDefinition("/**","authc");
return definotion;
}
}
创建一个Controller
@RestController
public class LoginController{
@GetMappin("/hello")
public String hello(){
return "hello shiro";
}
@GetMappin("/login")
public String hello(){
return "please login";
}
@PostMapping("/doLogin")
public String doLogin(String username,String password){
Subject subject = SecurityUtils.getSubject();
try{
subject.login(new UsernamePasswordToken(username,password));
System.out.println("success");
}catch(AuthenticationException e){
e.printStackTrace()
System.out.println("fail>>"+e.getMessage());
}
}
- Spring Security 使用JSON登录
- UsernamePasswordAuthenticationFilter 创建一个类
public class MyAuthenticationFiler extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAutentication(HttpServletRequest request,HttpServletResponse response)throws
AuthenticaionException {
if(!request.getMethod().equals("POST")){
throw new AuthenticationServiceException(
"不是Post请求"+request.getMethod()
);
}
if(request.getContentType().equals(MedialType.APPLICATION_JSON_VALUE)){
//说明用户以JSON的形式传递参数
String username= null;
String password = null);
try{
Map<String,String> map = new ObjectMapper().readValue(request.getInputStream(),Map.class);
username=map.get("username");
password=map.get("password");
}catch(IOException e){
e.printStackTrace();
}
if(username==null){
username="";
}
if(password==null){
password="";
}
username=username.trim();
UsernmePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);
setDetails(request,authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
return super.attemptAuthentication(request,response);
}
}
- 创建一个SecurityConfig配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and().scrf().disable();
http.addFilterAt(myAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
}
@Bean
MyAuthenticationFilter myAuthenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
}
创建一个Controller
@RestController
public class HelloController{
@GetMappin("/hello")
public String hello(){
return "hello Security";
}
}
{"username":"zenghao","password":"123"}