【1】Spring Boot系列之Security

Spring Boot下载地址:https://start.spring.io/

【0】pom.xml

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	
	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	
	<!-- 热部署,可去掉 -->
	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
   	</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>
</dependencies>

<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<configuration>
				<fork>true</fork>
           	</configuration>
		</plugin>
	</plugins>
</build>

【1】BrowerSecurityConfig.java

/**
* Spring Security默认是禁用注解的,要想开启注解,需要在继承WebSecurityConfigurerAdapter的类上
* 加@EnableGlobalMethodSecurity注解, 来判断用户对某个控制层的方法是否具有访问权限。
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class BrowerSecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Autowired
	private MyUserDetailsService myUserDetailsService;
	
	@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        	.formLogin()                    //  定义当需要用户登录时候,转到的登录页面。
    			//.loginPage("/login.html")   //  登录页(默认Security会提供一个登录页)
        		//.usernameParameter("username")
                //.passwordParameter("password")
                //.failureForwardUrl("/login?error")
            	.and() //and()返回了HttpSecurity本身
            .logout()
            	//.logoutUrl("/logout")
                //.logoutSuccessUrl("/index")
            	.permitAll()
            	.and()
            .exceptionHandling()
            	.accessDeniedHandler(new MyAccessDeniedHandler()) //访问拒绝处理
            	//.accessDeniedPage("/403")
                //.accessDeniedHandler()
                .and()
            .authorizeRequests() //配置路径拦截,表明路径访问所对应的权限,角色,认证信息。
    			.antMatchers("/**/common/*", "/index.html").permitAll() //允许所有用户访问"**/common/*"和"/index.html"
    			.anyRequest().authenticated();   // 其他地址的访问均需验证权限
    }
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
}

【2】MyAccessDeniedHandler.java

@Service
public class MyAccessDeniedHandler implements AccessDeniedHandler {
	
	@Override
	public void handle(HttpServletRequest request, 
			HttpServletResponse response, 
			AccessDeniedException accessDeniedException) throws IOException, ServletException {
		
		if (HTTPUtils.isAjaxRequest(request)) {//AJAX请求,使用response发送403
			response.sendError(403);
			/*response.setHeader("noAuthentication", "true");  
            ResultWithObject resultWithObject = new ResultWithObject(CC.NEGATIVE_1, CC.RESULT_MESSAGE_TEXT_DEFAULT);  
            resultWithObject.setMsg("您无权限访问该请求!");  
            response.setContentType("application/json;charset=UTF-8");  
            PrintWriter writer = response.getWriter();  
            writer.write(JasonUtils.Object2String(resultWithObject));  
            writer.close();  
            response.flushBuffer();  */
		} else if (!response.isCommitted()) {//非AJAX请求,跳转系统默认的403错误界面,在web.xml中配置
			response.sendRedirect("/403"); //相当于 .accessDeniedPage("/403")
		}
	}
}

【3】MyUserDetailsService.java

@Service
public class MyUserDetailsService implements UserDetailsService{
	
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
    private PasswordEncoder passwordEncoder;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		logger.info("用户的用户名: {}", username);
	
	    String password = passwordEncoder.encode("123456");
	    logger.info("password: {}", password);
	
	    /**
	     *  参数分别是:用户名,密码,用户权限(此处将其设置为:aaa)
	     */
	    User user = new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("aaa"));
	    return user;
	}

	/*@Autowired
    private UserService userService;

    *//**
     * 授权的时候是对角色授权,而认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的
     *//*
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUser = userService.getUserByName(username);
        if (null == sysUser) {
            throw new UsernameNotFoundException(username);
        }
        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        for (SysRole role : sysUser.getRoleList()) {
            for (SysPermission permission : role.getPermissionList()) {
                authorities.add(new SimpleGrantedAuthority(permission.getCode()));
            }
        }

        return new User(sysUser.getUsername(), sysUser.getPassword(), authorities);
    }*/
}

【4】HTTPUtils.java

public class HTTPUtils {
	
	/**
	 *  判断是否为ajax请求
	 */
	public static boolean isAjaxRequest(HttpServletRequest request) {
		if (request.getHeader("accept").indexOf("application/json") > -1 
				|| ( request.getHeader("X-Requested-With") != null 
					&& request.getHeader("X-Requested-With").equals("XMLHttpRequest") ) ) {
			return true;
		}
		return false;
	}
}

【5】HelloController.java

@Controller
public class HelloController {

	@RequestMapping("/403")
    public String to403(HashMap<String, Object> map) {
        map.put("message", "欢迎进入403页面");
        return "/403";
    }
	
	/**
	*(1) @PreAuthorize("hasAuthority('ddd')"):具有ddd权限的才能访问; 
	*(2)另有 @PreAuthorize("hasRole('userRole')"):具有userRole权限才能访问
	*/
	@PreAuthorize("hasAuthority('ddd')")
	@RequestMapping("/hello/d")
    public String toHello(HashMap<String, Object> map) {
		map.put("message", "欢迎进入hello页面");
		return "/hello";
    }
}

【6】HelloRestController.java

@RestController
@RequestMapping("/demo")
public class HelloRestController {
	
	@RequestMapping("/common/a")
    public String common() {
        return "Hello World1";
    }

	@PreAuthorize("hasAuthority('aaa')")
	@RequestMapping("/index/a")
    public String indexA() {
        return "Hello WorldA";
    }

	@PreAuthorize("hasAuthority('bbb')")
	@RequestMapping("/index/b")
    public String indexB() {
        return "Hello WorldC";
    }
}

【7】
application.properties

#thymeleaf配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.cache=false
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html

注意点:
(1)要使用 @PreAuthorize 注解,必须为继承WebSecurityConfigurerAdapter 的类添加以下注解:EnableGlobalMethodSecurity(prePostEnabled = true)

(2)@Controller 与 @RestController:
@RestController注解相当于@ResponseBody + @Controller合在一起的作用。如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。
RestController 注解源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 * @since 4.0.1
	 */
	@AliasFor(annotation = Controller.class)
	String value() default "";

}

(3)@EnableGlobalMethodSecurity
1)@EnableGlobalMethodSecurity(securedEnabled=true) 开启@Secured 注解过滤权限;
2)@EnableGlobalMethodSecurity(jsr250Enabled=true) 开启@RolesAllowed 注解过滤权限;
3)@EnableGlobalMethodSecurity(prePostEnabled=true) 使用表达式时间方法级别的安全性,有4个注解可用。

(4)prePostEnabled注解
@PreAuthorize:
在方法执行之前执行,而且这里可以调用方法的参数,也可以得到参数值,这是利用JAVA8的参数名反射特性,如果没用JAVA8,那么也可以利用Spring Security的@P标注参数,或者Spring Data的@Param标注参数。

@PreAuthorize("#userId == authentication.principal.userId or hasAuthority(‘ADMIN’)")
void changePassword(@P("userId") long userId ){  }

这里表示在 changePassword 方法执行之前,判断方法参数userId 的值是否等于principal中保存的当前用户的 userId,或者当前用户是否具有ROLE_ADMIN权限,两种符合其一,就可以访问该方法。

@PostAuthorize:
在方法执行之后执行,但是如果表达式计算结果为false,将抛出一个安全性异常。EL变量 returnObject表示返回的对象。

@PostAuthorize
User getUser("returnObject.userId == authentication.principal.userId or hasPermission(returnObject, 'ADMIN')");

@PreFilter:
在方法执行之前执行,而且这里可以调用方法的参数,然后对参数值进行过滤或处理或修改,EL变量filterObject表示参数,如有多个参数,使用filterTarget注解参数。只有方法参数是集合或数组才行。(很少会用到,与分页技术不兼容)

@PostFilter
PostFilter 允许方法调用, 但必须按照表达式来过滤方法的结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值